/**
 * PrimeFaces TreeTable Widget
 */
PrimeFaces.widget.TreeTable = function(cfg) {
    this.cfg = cfg;
    this.id = this.cfg.id;
	this.jqId = PrimeFaces.escapeClientId(this.id);
    this.jq = $(this.jqId);
	this.cfg = cfg;
    this.cfg.scrollable = this.jq.hasClass('ui-treetable-scrollable');
    this.cfg.resizable = this.jq.hasClass('ui-treetable-resizable');
    
    this.bindToggleEvents();
        
    //scrolling
    if(this.cfg.scrollable) {
        this.alignColumnWidths();
        this.setupScrolling();
    }

    //selection
    if(this.cfg.selectionMode) {
        this.jqSelection = $(this.jqId + '_selection');
        var selectionValue = this.jqSelection.val();
        
        this.selection = selectionValue === "" ? [] : selectionValue.split(',');
        
        if(this.cfg.selectionMode == 'checkbox'){
        	this.preselectCheckboxPropagation();
        }
        
        this.bindSelectionEvents();
    }
    
    this.postConstruct();
}

PrimeFaces.extend(PrimeFaces.widget.TreeTable, PrimeFaces.widget.BaseWidget);

PrimeFaces.widget.TreeTable.prototype.preselectCheckboxPropagation = function() {
	var _self = this;
	
	var _children = [];
	// checked
	this.jq.find('.ui-tree-checkbox-icon').each(function() {
        var icon = $(this),
        node = icon.parents('tr:first'),
        nodeKey = node.attr('id').split('_node_')[1];
        
        if(_self.isSelected(nodeKey))
        	icon.addClass('ui-icon ui-icon-check');
    });
	
	// minus
	this.jq.find('.ui-tree-checkbox-icon').not('.ui-icon-check').each(function() {
        var icon = $(this),
        node = icon.parents('tr:first');

        if(node.parent().find(_self.getSubNodeSelectionHolder(node)+" .ui-icon-check").length > 0)
        	_self.preselectCheckboxMinusPropagation(node);
        else
        	icon.removeClass('ui-icon ui-icon-check ui-icon-minus');
    });
}

PrimeFaces.widget.TreeTable.prototype.preselectCheckboxMinusPropagation = function(node) {
	node.find('.ui-tree-checkbox-icon').addClass('ui-icon ui-icon-minus');
	
	var pNode = this.getParentNode(node);
	if(pNode)
		this.preselectCheckboxMinusPropagation(pNode);
	
}

PrimeFaces.widget.TreeTable.prototype.bindToggleEvents = function() {
    var _self = this;
    
    //expand and collapse
    $(this.jqId + ' .ui-treetable-toggler').die('click.treetable')
        .live('click.treetable', function(e) {
            var toggler = $(this),
            node = toggler.parents('tr:first');
            
            if(toggler.hasClass('ui-icon-triangle-1-e'))
                _self.expandNode(e, node);
            else {
                _self.collapseNode(e, node);
            }
        });
}

PrimeFaces.widget.TreeTable.prototype.bindSelectionEvents = function() {
    var _self = this;
    
    if(this.isCheckboxSelection()){
    	$(this.jqId + ' .ui-tree-checkbox-box').die('mouseover.treetable mouseout.treetable click.treetable contextmenu.treetable')
	        .live('mouseover.treetable', function(e) {
	            var element = $(this);
	
	            if(!element.hasClass('ui-selected')) {
	                element.addClass('ui-state-highlight');
	            }
	        })
	        .live('mouseout.treetable', function(e) {
	            var element = $(this);
	
	            if(!element.hasClass('ui-selected')) {
	                element.removeClass('ui-state-highlight');
	            }
	        })
	        .live('click.treetable', function(e) {
	            _self.onRowClick(e, $(this).parents('tr:first'));
	            e.preventDefault();
	        });
    } else {
    	$(this.jqId + ' .ui-treetable-data tr').die('mouseover.treetable mouseout.treetable click.treetable contextmenu.treetable')
	        .live('mouseover.treetable', function(e) {
	            var element = $(this);
	
	            if(!element.hasClass('ui-selected')) {
	                element.addClass('ui-state-highlight');
	            }
	        })
	        .live('mouseout.treetable', function(e) {
	            var element = $(this);
	
	            if(!element.hasClass('ui-selected')) {
	                element.removeClass('ui-state-highlight');
	            }
	        })
	        .live('click.treetable', function(e) {
	            _self.onRowClick(e, $(this));
	            e.preventDefault();
	        })            
	        .live('contextmenu.treetable', function(event) {
	           _self.onRowClick(event, $(this));
	           event.preventDefault();
	        });
    }
}

PrimeFaces.widget.TreeTable.prototype.expandNode = function(e, node) {
    var options = {
        source: this.id,
        process: this.id,
        update: this.id
    },
    _self = this,
    nodeKey = node.attr('id').split('_node_')[1],
    parentNode = this.getParentNode(node);
    
    options.onsuccess = function(responseXML) {
        var xmlDoc = $(responseXML.documentElement),
        updates = xmlDoc.find("update");

        for(var i=0; i < updates.length; i++) {
            var update = updates.eq(i),
            id = update.attr('id'),
            content = update.text();

            if(id == _self.id){
                node.replaceWith(content);
                node.find('.ui-treetable-toggler:first').addClass('ui-icon-triangle-1-s').removeClass('ui-icon-triangle-1-e');
                node.attr('aria-expanded', true);
            }
            else {
                PrimeFaces.ajax.AjaxUtils.updateElement.call(this, id, content);
            }
        }

        PrimeFaces.ajax.AjaxUtils.handleResponse.call(this, xmlDoc);
        
        return true;
    };
    
    options.oncomplete = function(responseXML) {
    	if(_self.cfg.selectionMode == 'checkbox'){
    		_self.preselectCheckboxPropagation();
    		
    		if(_self.isSelected(nodeKey)){
    			_self.toggleCheckbox($(PrimeFaces.escapeClientId(node.attr('id'))), true);
    			_self.writeSelections();
    		}
        }
    };
    
    var params = {};
    params[this.id + '_expand'] = nodeKey;
    if(parentNode){
    	 params[this.id + '_parent_expand'] = parentNode.attr('id').split('_node_')[1];
    }
    
    options.params = params;
    
    if(this.hasBehavior('expand')) {
        var expandBehavior = this.cfg.behaviors['expand'];
        
        expandBehavior.call(this, e, options);
    }
    else {
        PrimeFaces.ajax.AjaxRequest(options);
    }
}

PrimeFaces.widget.TreeTable.prototype.collapseNode = function(e, node) {
    node.siblings('[id^="' + node.attr('id') + '_"]').remove();

    node.find('.ui-treetable-toggler:first').addClass('ui-icon-triangle-1-e').removeClass('ui-icon-triangle-1-s');
    
    node.attr('aria-expanded', false);
    
    if(this.hasBehavior('collapse')) {
        var collapseBehavior = this.cfg.behaviors['collapse'],
        nodeKey = node.attr('id').split('_node_')[1];
        
        var options = {
            params : {}
        };
        options.params[this.id + '_collapse'] = nodeKey;
        
        collapseBehavior.call(this, e, options);
    }
}

PrimeFaces.widget.TreeTable.prototype.onRowClick = function(e, node) {
    
    //Check if rowclick triggered this event not an element in row content
	if($(e.target).is('div.ui-tt-c,td,div.ui-tree-checkbox-box,span.ui-tree-checkbox-icon')) {
        var nodeKey = node.attr('id').split('_node_')[1];
		if(this.isSelected(nodeKey))
            this.unselectNode(e, node);
        else
            this.selectNode(e, node);
        
        PrimeFaces.clearSelection();
    }
}

PrimeFaces.widget.TreeTable.prototype.selectNode = function(e, node) {
    var nodeKey = node.attr('id').split('_node_')[1],
    metaKey = (e.metaKey||e.ctrlKey);
    
    //unselect previous selection
    if(this.isSingleSelection() || (this.isMultipleSelection() && !metaKey)) {
        node.siblings('.ui-state-highlight').removeClass('ui-state-highlight').attr('aria-selected', false);
        this.selection = [];
    }

    //add to selection
    if(this.isCheckboxSelection()){
    	this.toggleCheckbox(node, true);
    } else {
	    node.addClass('ui-state-highlight ui-selected');
	    this.addSelection(nodeKey);
    }

    //save state
    this.writeSelections();
    
    this.fireSelectNodeEvent(e, nodeKey);
}

PrimeFaces.widget.TreeTable.prototype.unselectNode = function(e, node) {
    var nodeKey = node.attr('id').split('_node_')[1],
    metaKey = metaKey = (e.metaKey||e.ctrlKey);
    
    if(metaKey) {
        //remove visual style
        node.removeClass('ui-state-highlight');
        
        //aria
        node.attr('aria-selected', false);

        //remove from selection
        this.removeSelection(nodeKey);

        //save state
        this.writeSelections();
        
        this.fireUnselectNodeEvent(e, nodeKey);
    }
    else if(this.isMultipleSelection()){
        this.selectNode(e, node);
    }
    else if(this.isCheckboxSelection()){
    	this.toggleCheckbox(node, false);
    	
    	//save state
        this.writeSelections();
        
        this.fireUnselectNodeEvent(e, nodeKey);
    }
}

PrimeFaces.widget.TreeTable.prototype.toggleCheckbox = function(node, check) {

	//propagate selection this
    this.toggleThisCheckbox(node, check);
    
    //propagate selection down
    this.toggleChildrenCheckbox(node, check);

    //propagate selection up
    this.toggleParentCheckbox(node, check);
    
    //clear not display node selection
    var selection = this.selection,
    newselection = [],
    prefix = node.attr('id').split('_node_')[0] + '_node_';
    $.each(selection, function(index, value) {
        if($(PrimeFaces.escapeClientId(prefix + value)).size() > 0){
        	newselection.push(value);
        }
    });
    this.selection = newselection;
}

PrimeFaces.widget.TreeTable.prototype.toggleThisCheckbox = function(node, check) {
	var nodeKey = node.attr('id').split('_node_')[1];
    if(check) {
        this.addSelection(nodeKey);
    	node.find('.ui-tree-checkbox-icon').addClass('ui-icon ui-icon-check');
    	node.addClass('ui-selected ui-state-highlight');
    }
    else {
    	node.find('.ui-tree-checkbox-icon').removeClass('ui-icon ui-icon-check ui-icon-minus');
    	node.removeClass('ui-selected ui-state-highlight');
        this.removeSelection(nodeKey);
    }
}

PrimeFaces.widget.TreeTable.prototype.toggleChildrenCheckbox = function(node, check) {
	var _self = this;
	node.parent().find(this.getSubNodeSelectionHolder(node)+" .ui-tree-checkbox-icon").each(function() {
        var icon = $(this),
        node = icon.parents('tr:first'),
        nodeKey = node.attr('id').split('_node_')[1];
        
        if(check) {
            icon.addClass('ui-icon ui-icon-check');
            node.addClass('ui-selected ui-state-highlight');
            _self.addSelection(nodeKey);
        }
        else {
            icon.removeClass('ui-icon ui-icon-check ui-icon-minus');
            node.removeClass('ui-selected ui-state-highlight');
            _self.removeSelection(nodeKey);
        }
        
        _self.toggleChildrenCheckbox(node, check);
    });
}

PrimeFaces.widget.TreeTable.prototype.toggleParentCheckbox = function(node, check) {
	var parentNode = this.getParentNode(node);
	
    if(parentNode){
    	
    	var holder = this.getSubNodeSelectionHolder(parentNode),
    	parentNodeKey = parentNode.attr('id').split('_node_')[1],
        icon = parentNode.find('.ui-tree-checkbox-icon:first'),
        checkedChildren = parentNode.parent().children(holder).find('.ui-icon-check'),
        allChildren = parentNode.parent().children(holder).find('.ui-tree-checkbox-icon');

        if(check) {
            if(checkedChildren.length == allChildren.length) {
                icon.removeClass('ui-icon ui-icon-minus').addClass('ui-icon ui-icon-check');
                parentNode.addClass('ui-selected ui-state-highlight');
                this.addSelection(parentNodeKey);
            } else {
                icon.removeClass('ui-icon ui-icon-check').addClass('ui-icon ui-icon-minus');
                parentNode.removeClass('ui-selected ui-state-highlight');
                this.removeSelection(parentNodeKey);
            }
        }
        else {
        	var minusChildren = parentNode.parent().children(holder).find('.ui-icon-minus');
            if(checkedChildren.length > 0 || minusChildren.length > 0) {
                icon.removeClass('ui-icon ui-icon-check').addClass('ui-icon ui-icon-minus');
            } else {
                icon.removeClass('ui-icon ui-icon-minus ui-icon-check');
            }

            parentNode.removeClass('ui-selected ui-state-highlight');
            this.removeSelection(parentNodeKey);
        }
        
        this.toggleParentCheckbox(parentNode, check);
    }
}

PrimeFaces.widget.TreeTable.prototype.getSubNodeSelectionHolder = function(node) {
	return ".parent_node_" + node.attr('id').split('_node_')[1];
}

PrimeFaces.widget.TreeTable.prototype.getParentNode = function(node) {
	var classNames = node.attr('class').split(' ');
    
    for(var keyIdx = 0; keyIdx < classNames.length; keyIdx++) {
    	if(classNames[keyIdx] && classNames[keyIdx].match('parent_node_')) {
    		var parentNodeId = node.attr('id').split('_node_')[0] + '_node_' + classNames[keyIdx].split('_node_')[1];
    		return $(PrimeFaces.escapeClientId(parentNodeId));
    	}
    }
    
    return undefined;
}

PrimeFaces.widget.TreeTable.prototype.hasBehavior = function(event) {
    if(this.cfg.behaviors) {
        return this.cfg.behaviors[event] != undefined;
    }
    
    return false;
}

/**
 * Remove given rowIndex from selection
 */
PrimeFaces.widget.TreeTable.prototype.removeSelection = function(nodeKey) {
	this.selection = $.grep(this.selection, function(r) {
        return r != nodeKey;
    });
}

/**
 * Adds given rowIndex to selection if it doesn't exist already
 */
PrimeFaces.widget.TreeTable.prototype.addSelection = function(nodeKey) {
    if(!this.isSelected(nodeKey)) {
        this.selection.push(nodeKey);
    }
}

PrimeFaces.widget.TreeTable.prototype.isSelected = function(nodeKey) {
    var selection = this.selection,
    selected = false;
    
    $.each(selection, function(index, value) {
        if(value === nodeKey) {
            selected = true;
            
            return false;       //break
        } 
        else {
            return true;        //continue
        }
    });
    
    return selected;
}

PrimeFaces.widget.TreeTable.prototype.isSingleSelection = function() {
    return this.cfg.selectionMode == 'single';
}

PrimeFaces.widget.TreeTable.prototype.isMultipleSelection = function() {
    return this.cfg.selectionMode == 'multiple';
}

PrimeFaces.widget.TreeTable.prototype.isCheckboxSelection = function() {
    return this.cfg.selectionMode == 'checkbox';
}

/**
 * Writes selected row ids to state holder
 */
PrimeFaces.widget.TreeTable.prototype.writeSelections = function() {
    this.jqSelection.val(this.selection.join(','));
}

PrimeFaces.widget.TreeTable.prototype.fireSelectNodeEvent = function(e, nodeKey) {
    if(this.hasBehavior('select')) {
        var selectBehavior = this.cfg.behaviors['select'],
        options = {
            params : {}
        };
        
        options.params[this.id + '_instantSelect'] = nodeKey;
        
        selectBehavior.call(this, e, options);
    }
}

PrimeFaces.widget.TreeTable.prototype.fireUnselectNodeEvent = function(e, nodeKey) {
    if(this.hasBehavior('unselect')) {
        var unselectBehavior = this.cfg.behaviors['unselect'],
        options = {
            params : {}
        };
        
        options.params[this.id + '_instantUnselect'] = nodeKey;
        
        unselectBehavior.call(this, e, options);
    }
}

PrimeFaces.widget.TreeTable.prototype.setupScrolling = function() {
    var scrollHeader = $(this.jqId + ' .ui-treetable-scrollable-header'),
    scrollBody = $(this.jqId + ' .ui-treetable-scrollable-body'),
    scrollFooter = $(this.jqId + ' .ui-treetable-scrollable-footer');
    
    if(this.cfg.scrollWidth) {
        scrollHeader.width(this.cfg.scrollWidth);
        scrollBody.width(this.cfg.scrollWidth);
        scrollFooter.width(this.cfg.scrollWidth);
    }
        
    scrollBody.scroll(function() {
        scrollHeader.scrollLeft(scrollBody.scrollLeft());
        scrollFooter.scrollLeft(scrollBody.scrollLeft());
    });
}

/**
 * Moves widths of columns to column wrappers
 */
PrimeFaces.widget.TreeTable.prototype.alignColumnWidths = function() {
    this.jq.find('div.ui-tt-c').each(function() {
        var wrapper = $(this),
        column = wrapper.parent();
        
        wrapper.width(column.width());
        column.width('');
    });
}